package org.edgar.example.DB.Oracle;

import org.geotools.data.jdbc.FilterToSQLException;
import org.geotools.data.oracle.OracleFilterToSQL;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.spatial.*;

import static org.junit.jupiter.api.Assertions.assertEquals;

/**
 *  测试来源 geotools OracleFilterToSqlTest
 */
public class OracleFilterToSqlTest {

    OracleFilterToSQL encoder;

    FilterFactory2 ff;

    GeometryFactory gf;

    @BeforeEach
    protected void setUp() throws Exception {
        encoder = new OracleFilterToSQL(null);
        ff = CommonFactoryFinder.getFilterFactory2(null);
        gf = new GeometryFactory();
    }

    @Test
    public void testIncludeEncoding() throws Exception {
        // nothing to filter, no WHERE clause
        assertEquals("WHERE 1 = 1", encoder.encodeToString(org.opengis.filter.Filter.INCLUDE));
    }

    @Test
    public void testExcludeEncoding() throws Exception {
        assertEquals("WHERE 0 = 1", encoder.encodeToString(org.opengis.filter.Filter.EXCLUDE));
    }

    @Test
    public void testBboxFilter() throws Exception {
        try {
            BBOX bbox = ff.bbox("GEOM", -180, -90, 180, 90, "EPSG:4326");
            String encoded = encoder.encodeToString(bbox);
            assertEquals(
                    "WHERE SDO_RELATE(\"GEOM\", ?, 'mask=anyinteract querytype=WINDOW') = 'TRUE' ",
                    encoded);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void testLooseBboxFilter() throws Exception {
        BBOX bbox = ff.bbox("GEOM", -180, -90, 180, 90, "EPSG:4326");
        encoder.setLooseBBOXEnabled(true);
        String encoded = encoder.encodeToString(bbox);
        assertEquals(
                "WHERE SDO_FILTER(\"GEOM\", ?, 'mask=anyinteract querytype=WINDOW') = 'TRUE' ",
                encoded);
    }

    @Test
    public void testContainsFilter() throws Exception {
        // ECQL格式
        String ecql = "contains(SHAPE, POINT(10 -10))";
        // 转换流程
        Filter filter = ECQL.toFilter(ecql);
        String oracleSql = encoder.encodeToString(filter);
        // 输出: WHERE SDO_RELATE("SHAPE", ?, 'mask=contains querytype=WINDOW') = 'TRUE'

        Contains contains =
                ff.contains(
                        ff.property("SHAPE"),
                        ff.literal(gf.createPoint(new Coordinate(10.0, -10.0))));
        String encoded = encoder.encodeToString(contains);
        assertEquals(
                "WHERE SDO_RELATE(\"SHAPE\", ?, 'mask=contains querytype=WINDOW') = 'TRUE' ",
                encoded);
    }

    @Test
    public void testCrossesFilter() throws Exception {
        // ECQL格式
        String ecql = "crosses(GEOM, LINESTRING(-10 -10, 10 10))";
        // 转换流程
        Filter filter = ECQL.toFilter(ecql);
        String oracleSql = encoder.encodeToString(filter);
        // 输出: WHERE SDO_RELATE("GEOM", ?, 'mask=overlapbdydisjoint querytype=WINDOW') = 'TRUE'


        Crosses crosses =
                ff.crosses(
                        ff.property("GEOM"),
                        ff.literal(
                                gf.createLineString(
                                        new Coordinate[] {
                                                new Coordinate(-10.0d, -10.0d), new Coordinate(10d, 10d)
                                        })));
        String encoded = encoder.encodeToString(crosses);
        assertEquals(
                "WHERE SDO_RELATE(\"GEOM\", ?, 'mask=overlapbdydisjoint querytype=WINDOW') = 'TRUE' ",
                encoded);
    }

    @Test
    public void testIntersectsFilter() throws Exception {
        // ECQL格式
        String ecql = "intersects(GEOM, LINESTRING(-10 -10, 10 10))";
        // 转换流程
        Filter filter = ECQL.toFilter(ecql);
        String oracleSql = encoder.encodeToString(filter);
        // 输出: WHERE SDO_RELATE("GEOM", ?, 'mask=anyinteract querytype=WINDOW') = 'TRUE'

        Intersects intersects =
                ff.intersects(
                        ff.property("GEOM"),
                        ff.literal(
                                gf.createLineString(
                                        new Coordinate[] {
                                                new Coordinate(-10.0d, -10.0d), new Coordinate(10d, 10d)
                                        })));
        String encoded = encoder.encodeToString(intersects);
        assertEquals(
                "WHERE SDO_RELATE(\"GEOM\", ?, 'mask=anyinteract querytype=WINDOW') = 'TRUE' ",
                encoded);
    }

    @Test
    public void testOverlapsFilter() throws Exception {

        // ECQL格式
        String ecql = "overlaps(GEOM, LINESTRING(-10 -10, 10 10))";
        // 转换流程
        Filter filter = ECQL.toFilter(ecql);
        String oracleSql = encoder.encodeToString(filter);
        // 输出: WHERE SDO_RELATE("GEOM", ?, 'mask=overlapbdyintersect querytype=WINDOW') = 'TRUE'

        Overlaps overlaps =
                ff.overlaps(
                        ff.property("GEOM"),
                        ff.literal(
                                gf.createLineString(
                                        new Coordinate[] {
                                                new Coordinate(-10.0d, -10.0d), new Coordinate(10d, 10d)
                                        })));
        String encoded = encoder.encodeToString(overlaps);
        assertEquals(
                "WHERE SDO_RELATE(\"GEOM\", ?, 'mask=overlapbdyintersect querytype=WINDOW') = 'TRUE' ",
                encoded);
    }

    @Test
    public void testDWithinFilterWithUnit() throws Exception {
        Coordinate coordinate = new Coordinate();
        DWithin dwithin =
                ff.dwithin(
                        ff.property("GEOM"),
                        ff.literal(gf.createPoint(coordinate)),
                        10.0,
                        "kilometers");
        String encoded = encoder.encodeToString(dwithin);
        assertEquals(
                "WHERE SDO_WITHIN_DISTANCE(\"GEOM\",?,'distance=10.0 unit=km') = 'TRUE' ", encoded);
    }

    @Test
    public void testDWithinFilterWithUnitEscaping() throws Exception {
        Coordinate coordinate = new Coordinate();
        DWithin dwithin =
                ff.dwithin(
                        ff.property("GEOM"), ff.literal(gf.createPoint(coordinate)), 10.0, "'FOO");
        String encoded = encoder.encodeToString(dwithin);
        assertEquals(
                "WHERE SDO_WITHIN_DISTANCE(\"GEOM\",?,'distance=10.0 unit='FOO') = 'TRUE' ",
                encoded);
    }

    @Test
    public void testDWithinFilterWithoutUnit() throws Exception {
        Coordinate coordinate = new Coordinate();
        DWithin dwithin =
                ff.dwithin(ff.property("GEOM"), ff.literal(gf.createPoint(coordinate)), 10.0, null);
        String encoded = encoder.encodeToString(dwithin);
        assertEquals("WHERE SDO_WITHIN_DISTANCE(\"GEOM\",?,'distance=10.0') = 'TRUE' ", encoded);
    }

    @Test
    public void StringFilterToString() throws CQLException, FilterToSQLException {
        String strFilter = "bbox(GEOM, -180, -90, 180, 90)";
        // 解析ECQL为Filter对象
        Filter filter = ECQL.toFilter(strFilter);
        String encoded = encoder.encodeToString(filter);
        System.out.println(encoded);

//        bbox → SDO_RELATE/SDO_FILTER
//        contains → SDO_RELATE with 'mask=contains'
//        intersects → SDO_RELATE with 'mask=anyinteract'
//        crosses → SDO_RELATE with 'mask=overlapbdydisjoint'
//        overlaps → SDO_RELATE with 'mask=overlapbdyintersect'
//        dwithin → SDO_WITHIN_DISTANCE
    }
}
